package xijia.plus.docs.manage.all.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import io.github.wslxm.springbootplus2.core.base.service.impl.BaseServiceImpl;
import io.github.wslxm.springbootplus2.core.config.error.ErrorException;
import io.github.wslxm.springbootplus2.core.enums.Base;
import io.github.wslxm.springbootplus2.core.utils.BeanDtoVoUtil;
import io.github.wslxm.springbootplus2.core.utils.tree.TreeUtil;
import io.github.wslxm.springbootplus2.core.utils.validated.ValidUtil;
import io.github.wslxm.springbootplus2.starter.redis.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xijia.plus.docs.constant.CacheKey;
import xijia.plus.docs.enums.Yw;
import xijia.plus.docs.manage.all.mapper.DocInfoMapper;
import xijia.plus.docs.manage.all.model.dto.DocInfoDTO;
import xijia.plus.docs.manage.all.model.dto.DocInfoUpdHtmlDTO;
import xijia.plus.docs.manage.all.model.entity.DocCategory;
import xijia.plus.docs.manage.all.model.entity.DocInfo;
import xijia.plus.docs.manage.all.model.query.DocInfoQuery;
import xijia.plus.docs.manage.all.model.vo.DocInfoClientVO;
import xijia.plus.docs.manage.all.model.vo.DocInfoVO;
import xijia.plus.docs.manage.all.service.DocCategoryService;
import xijia.plus.docs.manage.all.service.DocInfoService;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;


/**
 * 文档表 ServiceImpl
 *
 * <p>
 * ::本代码由[兮家小二]提供的代码生成器生成,如有问题,请手动修改 ::作者CSDN:https://blog.csdn.net/qq_41463655
 * </p>
 *
 * @author ws
 * @email 1720696548@qq.com
 * @date 2022-10-18 15:11:33
 */
@Service
@Slf4j
//@DS("#header.tenantName")
public class DocInfoServiceImpl extends BaseServiceImpl<DocInfoMapper, DocInfo> implements DocInfoService {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private DocCategoryService docCategoryService;

    @Override
    @DS("#tenantName")
    public List<DocInfoVO> listKeyData(String tenantName, DocInfoQuery query) {
        LambdaQueryWrapper<DocInfo> wrapper = new LambdaQueryWrapper<DocInfo>().select(DocInfo::getId, DocInfo::getName, DocInfo::getAuthor);
        List<DocInfo> list = this.list(wrapper);
        return BeanDtoVoUtil.listVo(list, DocInfoVO.class);
    }

    @Override
    @Cacheable(value = CacheKey.XJ_DOC_INFO_TREE, key = "#tenantName")
    @DS("#tenantName")
    public Map<Integer, List<DocInfoClientVO>> clientTree(String tenantName) {
        List<DocInfo> list = this.list(new LambdaQueryWrapper<DocInfo>()
                .select(DocInfo::getName, DocInfo::getId, DocInfo::getPid, DocInfo::getType, DocInfo::getShowType)
                .eq(DocInfo::getDisable, Base.Disable.V0.getValue())
                .orderByAsc(DocInfo::getSort)
                .orderByAsc(DocInfo::getName)
        );
        List<DocInfoClientVO> docInfoClientVOS = BeanDtoVoUtil.listVo(list, DocInfoClientVO.class);
        List<DocCategory> docCategoryList = docCategoryService.list(new LambdaQueryWrapper<DocCategory>()
                .select(DocCategory::getId, DocCategory::getCode, DocCategory::getName));
        Map<Integer, DocCategory> docCategoryMap = docCategoryList.stream().collect(Collectors.toMap(DocCategory::getCode, p -> p));
        for (DocInfoClientVO docInfoVo : docInfoClientVOS) {
            DocCategory docCategory = docCategoryMap.get(docInfoVo.getType());
            docInfoVo.setTypeName(docCategory.getName());
        }

        Map<Integer, List<DocInfoClientVO>> listMap = docInfoClientVOS.stream().collect(Collectors.groupingBy(DocInfoClientVO::getType));
        listMap.forEach((k, v) -> {
            List<DocInfoClientVO> children = TreeUtil.nextTree(v, "0").getChildren();
            listMap.put(k, children);
        });
        return listMap;
    }

    @Override
    @DS("#tenantName")
    public List<DocInfoVO> tree(String tenantName, DocInfoQuery query) {
        Integer type = query.getType() != null ? query.getType() : 1;
        Integer disable = query.getDisable();
        Integer showType = query.getShowType();
        String pid = query.getPid();
        LambdaQueryWrapper<DocInfo> queryWrapper = new LambdaQueryWrapper<DocInfo>()
                .eq(DocInfo::getType, type)
                .eq(showType != null, DocInfo::getShowType, showType)
                .and(StringUtils.isNotBlank(pid), p -> p.eq(DocInfo::getPid, pid)).or().eq(DocInfo::getId, pid)
                .eq(disable != null, DocInfo::getDisable, disable);
        queryWrapper.select(DocInfo.class, info -> !"content".equals(info.getColumn()) && !"content_html".equals(info.getColumn()));
        queryWrapper.orderByAsc(DocInfo::getSort);
        queryWrapper.orderByAsc(DocInfo::getName);
        // 1、查询菜单
        List<DocInfo> docInfos = this.list(queryWrapper);
        if (docInfos.size() == 0) {
            return null;
        }
        List<DocInfoVO> btDocInfoVOS = BeanDtoVoUtil.listVo(docInfos, DocInfoVO.class);
        if (pid == null) {
            return TreeUtil.nextTree(btDocInfoVOS, "0").getChildren();
        } else {
            return TreeUtil.nextTree(btDocInfoVOS, pid).getChildren();
        }
    }

    @Override
    @DS("#tenantName")
    public List<DocInfoVO> listIdNames(String tenantName) {
        List<DocInfo> list = this.list(new LambdaQueryWrapper<DocInfo>().select(DocInfo::getId, DocInfo::getName));
        return BeanDtoVoUtil.listVo(list, DocInfoVO.class);
    }


    @Override
    @DS("#tenantName")
    public DocInfoVO findId(String tenantName, String id) {
        DocInfoVO docInfoVO = BeanDtoVoUtil.convert(this.getById(id), DocInfoVO.class);
        List<DocInfo> docInfo = this.list(new LambdaQueryWrapper<DocInfo>()
                .select(DocInfo::getId, DocInfo::getPid, DocInfo::getName));
        // 增加父级数据
        List<DocInfoVO> docInfoVOS = BeanDtoVoUtil.listVo(docInfo, DocInfoVO.class);
        DocInfoVO newDocInfoVO = TreeUtil.fatherTree(docInfoVOS, docInfoVO.getId());
        docInfoVO.setParentNode(newDocInfoVO.getParentNode());
        return docInfoVO;
    }


    @Override
    @Cacheable(value = CacheKey.XJ_DOC_INFO_NAME, key = "#tenantName +'_'+#categoryName +'_' +#name", unless = "#result == null")
    @DS("#tenantName")
    public DocInfoVO findByName(String tenantName, String categoryName, String name) {
        // 获取类别
        Integer categoryCode = docCategoryService.findCategoryCode(tenantName, categoryName);
        if (categoryCode == null) {
            throw new ErrorException("typeName 错误: 没有找到名为: " + categoryName + " 的类别");
        }

        // 根据类别+ 名称查询
        DocInfo docInfo = this.getOne(new LambdaQueryWrapper<DocInfo>()
                .eq(DocInfo::getName, name)
                .eq(DocInfo::getType, categoryCode)
                .eq(DocInfo::getShowType, Yw.DocShowType.V2.getValue())
                .last("limit 1")
        );
        ValidUtil.isTrue(docInfo == null, "没有找到数据");
        DocInfoVO docInfoVO = BeanDtoVoUtil.convert(docInfo, DocInfoVO.class);
        docInfoVO.setTypeName(categoryName);
        return docInfoVO;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    // @CacheEvict(value = CacheKey.XJ_DOC_INFO_TREE, allEntries = true)
    @DS("#tenantName")
    public String insert(String tenantName, DocInfoDTO dto) {
        this.verifyName(dto.getType(), dto.getName(), null);
        // 添加数据
        DocInfo entity = dto.convert(DocInfo.class);
        boolean b = this.save(entity);

        // 删除缓存
        this.delCacheAll();

        return entity.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    //@CacheEvict(value = CacheKey.XJ_DOC_INFO_TREE, allEntries = true)
    @DS("#tenantName")
    public boolean upd(String tenantName, String id, DocInfoDTO dto) {
        this.verifyName(dto.getType(), dto.getName(), id);
        long count = this.count(new LambdaQueryWrapper<DocInfo>().eq(DocInfo::getId, id));
        ValidUtil.isTrue(count == 0, "没有找到数据");
        DocInfo entity = dto.convert(DocInfo.class);
        entity.setId(id);
        // 删除缓存
        this.delCacheAll();
        return this.updateById(entity);
    }

    @Override
    @DS("#tenantName")
    public boolean updHtml(String tenantName, String id, DocInfoUpdHtmlDTO dto) {
        DocInfo entity = new DocInfo();
        entity.setId(id);
        entity.setContentHtml(dto.getContentHtml());
        // 删除缓存
        this.delCacheAll();
        return this.updateById(entity);
    }

    @Override
    //@CacheEvict(value = CacheKey.XJ_DOC_INFO_TREE, allEntries = true)
    @DS("#tenantName")
    public boolean updPid(String tenantName, String id, String pid) {
        long count = this.count(new LambdaQueryWrapper<DocInfo>().eq(DocInfo::getId, id));
        ValidUtil.isTrue(count == 0, "没有找到数据");
        DocInfo docInfo = new DocInfo();
        docInfo.setId(id);
        docInfo.setPid(pid);
        // 删除缓存
        this.delCacheAll();
        return this.updateById(docInfo);
    }

    @Override
    //@CacheEvict(value = CacheKey.XJ_DOC_INFO_TREE, allEntries = true)
    @DS("#tenantName")
    public boolean updDisable(String tenantName, String id, Integer disable) {
        long count = this.count(new LambdaQueryWrapper<DocInfo>().eq(DocInfo::getId, id));
        ValidUtil.isTrue(count == 0, "没有找到数据");
        DocInfo docInfo = new DocInfo();
        docInfo.setId(id);
        docInfo.setDisable(disable);
        // 删除缓存
        this.delCacheAll();
        return this.updateById(docInfo);
    }

    @Override
    //@CacheEvict(value = CacheKey.XJ_DOC_INFO_TREE, allEntries = true)
    @DS("#tenantName")
    public boolean updSort(String tenantName, String id, Integer sort) {
        long count = this.count(new LambdaQueryWrapper<DocInfo>().eq(DocInfo::getId, id));
        ValidUtil.isTrue(count == 0, "没有找到数据");
        DocInfo docInfo = new DocInfo();
        docInfo.setId(id);
        docInfo.setSort(sort);
        // 删除缓存
        this.delCacheAll();
        return this.updateById(docInfo);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    //@CacheEvict(value = CacheKey.XJ_DOC_INFO_TREE, allEntries = true)
    @DS("#tenantName")
    public boolean del(String tenantName, String id) {
        // 检查分类下是否有试剂盒产品或文章数据
        DocInfo docInfo = this.getOne(new LambdaQueryWrapper<DocInfo>().eq(DocInfo::getId, id));
        ValidUtil.isTrue(docInfo == null, "没有找到数据");
        // 查询所有分类数据
        List<DocInfo> docInfos = this.list(new LambdaQueryWrapper<DocInfo>()
                .select(DocInfo::getId, DocInfo::getPid)
                .eq(DocInfo::getType, docInfo.getType())
        );
        List<DocInfoVO> docInfoVOS = BeanDtoVoUtil.listVo(docInfos, DocInfoVO.class);
        List<String> nextIds = TreeUtil.nextIds(docInfoVOS, id);
        nextIds.add(id);
        // 删除缓存
        this.delCacheAll();
        // 删除
        boolean b = this.removeByIds(nextIds);
        return b;
    }


    @Override
//    @Caching(evict = {
//            @CacheEvict(value = CacheKey.XJ_DOC_INFO_TREE, allEntries = true),
//            @CacheEvict(value = CacheKey.XJ_DOC_INFO_NAME, allEntries = true)
//    })
    @DS("#tenantName")
    public boolean delByType(String tenantName, Integer type) {
        this.remove(new LambdaUpdateWrapper<DocInfo>().eq(DocInfo::getType, type));
        // 删除缓存
        this.delCacheAll();
        return true;
    }


    /**
     * 验证名称是否重复
     *
     * @param name
     * @param excludeId
     * @return void
     * @author wangsong
     */
    private void verifyName(Integer type, String name, String excludeId) {
        long count = this.count(new LambdaQueryWrapper<DocInfo>()
                .eq(DocInfo::getName, name)
                .eq(DocInfo::getType, type)
                .ne(excludeId != null, DocInfo::getId, excludeId));
        ValidUtil.isTrue(count > 0, "文档名称重复");
    }


    /**
     * 通过id 删除当前数据以别名方式存储的的缓存
     *
     * @author wangsong
     * @mail 1720696548@qq.com
     * @date 2022/11/2 0002 16:24
     * @version 1.0.0
     */
    private void delCacheAll() {
        Set<String> listKey = redisUtil.getListKey(CacheKey.XJ_DOC_INFO_NAME);
        Set<String> treeKey = redisUtil.getListKey(CacheKey.XJ_DOC_INFO_TREE);
        redisUtil.delete(listKey);
        redisUtil.delete(treeKey);
    }

    @Override
    @DS("#tenantName")
    public List<DocInfo> list(String tenantName, Integer type) {
        LambdaQueryWrapper<DocInfo> queryWrapper = new LambdaQueryWrapper<DocInfo>()
                .select(DocInfo::getId, DocInfo::getPid, DocInfo::getShowType, DocInfo::getContent, DocInfo::getName, DocInfo::getDisable)
                .orderByAsc(DocInfo::getSort)
                .eq(DocInfo::getType, type)
                .eq(DocInfo::getDisable, Base.Disable.V0);
        // 查询
        return this.list(queryWrapper);
    }

    @Override
    @DS("#tenantName")
    public boolean remove(String tenantName, Integer type) {
        return this.remove(new LambdaQueryWrapper<DocInfo>().eq(DocInfo::getType, type));
    }

    @Override
    @DS("#tenantName")
    public boolean saveBatch(String tenantName, List<DocInfo> docInfos) {
        return this.saveBatch(docInfos);
    }
}
